package com.tungkong.usbcamera;

import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.SurfaceTexture;
import android.hardware.usb.UsbDevice;
import android.os.Environment;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;

import com.tungkong.libusbcamera.R;
import com.serenegiant.usb.DeviceFilter;
import com.serenegiant.usb.Size;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usb.common.UVCCameraHandler;
import com.serenegiant.usb.widget.CameraViewInterface;
import com.serenegiant.usb.widget.UVCCameraTextureView;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 创建日期：2021-08-31 11:57
 * 创建者：吴开杰
 * 修改日期：2021-08-31 11:57
 * 修改者：吴开杰
 * 版本：v.1.0
 * 描述：摄像头功能操作工具，包括单摄像头，多摄像头
 * 1.摄像头数据预览
 * 2.支持摄像头图像截取
 * 3.支持摄像头视频录制，包括，预览录制，和后台录制
 */
public class UVCCameraPreview {
    public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
    public static final String SUFFIX_JPEG = ".jpeg";
    public static final String SUFFIX_MP4 = ".mp4";
    private static final String TAG = "UVCCameraPreview";
    private int previewWidth = 640;
    private int previewHeight = 480;
    public static final int FRAME_FORMAT_YUYV = UVCCamera.FRAME_FORMAT_YUYV;
    // Default using MJPEG
    // if your device is connected,but have no images
    // please try to change it to FRAME_FORMAT_YUYV
    public static final int FRAME_FORMAT_MJPEG = UVCCamera.FRAME_FORMAT_MJPEG;
    public static final int MODE_BRIGHTNESS = UVCCamera.PU_BRIGHTNESS;
    public static final int MODE_CONTRAST = UVCCamera.PU_CONTRAST;
    private int mFrameFormat = FRAME_FORMAT_MJPEG;

    private static UVCCameraPreview mCameraHelper;
    // USB Manager
    private USBMonitor mUSBMonitor;

    private Activity mActivity;

    // 保存已经连接的UVC摄像头设备
    private HashMap<UsbDevice, USBMonitor.UsbControlBlock> connCameras = new HashMap<>();
    // 保存已经打开的UVC摄像头设备
    private List<USBMonitor.UsbControlBlock> openCameras = new ArrayList<>();
    // 保存已经打开摄像头的句柄，用于参控摄像头
    private HashMap<USBMonitor.UsbControlBlock, UVCCameraHandler> handlerCameras = new HashMap<>();
    // 保存预览填充区域
    private HashMap<USBMonitor.UsbControlBlock, CameraViewInterface> viewCameras = new HashMap<>();

    public List<UsbDevice> getConnectedUsbDevices(){
        List<UsbDevice> usbDevices = new ArrayList<>();
        if(connCameras.size() > 0){
            for (UsbDevice usb:connCameras.keySet()) {
                usbDevices.add(usb);
            }
        }
        return usbDevices;
    }

    private void addConnUsbDevice(UsbDevice usbDevice, USBMonitor.UsbControlBlock controlBlock){
        if(!connCameras.containsKey(usbDevice)){
            connCameras.put(usbDevice, controlBlock);
        }
    }

    private void removeConnUsbDevice(UsbDevice usbDevice){
        if(connCameras.containsKey(usbDevice)){
            connCameras.remove(usbDevice);
        }
    }

    public void openCamera(UsbDevice usbDevice){
        openCamera(usbDevice, null);
    }

    public void openCamera(UsbDevice usbDevice, CameraViewInterface viewInterface){
        if (usbDevice == null){
            throw new NullPointerException("openCamera UsbDevice is null!");
        }
        try {
            // wait for usb device connected
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        USBMonitor.UsbControlBlock controlBlock = getUsbControlBlock(usbDevice);
        if(controlBlock != null){
            Log.e(TAG, "openUsbDevice");
            openUsbDevice(controlBlock,viewInterface);
        } else {
            Log.e(TAG, "openUsbDevice Failed!");
        }

    }

    private void openUsbDevice(USBMonitor.UsbControlBlock controlBlock, CameraViewInterface viewInterface){
        if(!openCameras.contains(controlBlock)){
            openCameras.add(controlBlock);
        }
        UVCCameraHandler uvcCameraHandler = null;
        if(handlerCameras.containsKey(controlBlock)){
            uvcCameraHandler = handlerCameras.get(uvcCameraHandler);
        }
        // 创建相机工作线程
        if(viewInterface == null){
            uvcCameraHandler = createUVCCamera(uvcCameraHandler);
        } else {
            setCameraViewInterface(controlBlock, viewInterface);
            uvcCameraHandler = createUVCCamera(uvcCameraHandler, viewInterface);
        }
        if(uvcCameraHandler != null){
            if(!handlerCameras.containsKey(controlBlock)){
                handlerCameras.put(controlBlock, uvcCameraHandler);
                Log.e(TAG, "handlerCameras.put(controlBlock, uvcCameraHandler);");
            }
            // 向线程发送打开相机指令
            openCamera(uvcCameraHandler, controlBlock);
        } else{
            throw new NullPointerException("UVCCameraHandler cannot be null!");
        }
    }

    public void closeCamera(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        closeUVCCameraHandler(cameraHandler);
    }

    private void closeUsbDevice(USBMonitor.UsbControlBlock controlBlock){
        UVCCameraHandler cameraHandler = handlerCameras.get(controlBlock);
        if(cameraHandler == null)       // 工作线程未创建
            return;
        if(!cameraHandler.isOpened())   // 摄像头未打开
            return;
        // 删除已打开usb设备句柄
        if(!handlerCameras.containsKey(controlBlock)){
            // 停止工作线程
            closeUVCCameraHandler(handlerCameras.get(controlBlock));
            handlerCameras.remove(controlBlock);
        }
        // 删除已打开usb设备
        if(!openCameras.contains(controlBlock)){
            openCameras.remove(controlBlock);
        }
        // 删除图像预览区域
        if(!viewCameras.containsKey(controlBlock)){
            viewCameras.remove(controlBlock);
        }
    }

    private void closeUVCCameraHandler(UVCCameraHandler cameraHandler){
        if (cameraHandler != null) {
            cameraHandler.close();
            cameraHandler = null;
        }
    }

    /**
     * 设置图像预览区域
     */
    private void setCameraViewInterface(UsbDevice usbDevice, CameraViewInterface viewInterface){
        if(connCameras.containsKey(usbDevice)){
            USBMonitor.UsbControlBlock controlBlock = connCameras.get(usbDevice);
            if(!viewCameras.containsKey(controlBlock)){
                viewCameras.put(controlBlock, viewInterface);
            }
        } else {
            Log.e("UVCCameraPreview","USBMonitor.UsbControlBlock not connected!");
        }
    }

    /**
     * 设置图像预览区域
     */
    private void setCameraViewInterface(USBMonitor.UsbControlBlock controlBlock, CameraViewInterface viewInterface){
        if(connCameras.containsValue(controlBlock)){
            if(!viewCameras.containsKey(controlBlock)){
                viewCameras.put(controlBlock, viewInterface);
            }
        } else {
            Log.e("UVCCameraPreview","USBMonitor.UsbControlBlock not connected!");
        }
    }

    public UsbDevice getUsbDevice(int vid, int pid){
        UsbDevice usbDevice = null;
        int uc = getUsbDeviceCount();
        Log.e("getUsbDevice","uvc camera count: " + uc);
        if(uc <= 0){
            throw new NullPointerException("not matching usb device!");
        }
        for (UsbDevice device:getUsbDeviceList()) {
            Log.e("getUsbDevice","vid:"+ device.getVendorId() + ",pid:" +device.getProductId());
        }
        Log.e("getUsbDevice","--------------");
        for (UsbDevice device:connCameras.keySet()) {
            Log.e("getUsbDevice","vid:"+ vid + ",pid:" + pid);
            if ((device.getVendorId() == vid) && (device.getProductId() == pid)){
                usbDevice = device;
                Log.e("getUsbDevice","geted uvc camera!");
                break;
            }
        }
        return usbDevice;
    }

    private boolean matchUsbDevice(UsbDevice usbDevice){
       boolean match = false;
       List<UsbDevice> ul = getUsbDeviceList();
       if(ul != null){
           for (UsbDevice device:ul) {
               if ((device.getVendorId() == usbDevice.getVendorId()) && (device.getProductId() == usbDevice.getProductId())){
                   match = true;
               }
           }
       }
       return match;
    }

    public USBMonitor.UsbControlBlock getUsbControlBlock(UsbDevice usbDevice){
        USBMonitor.UsbControlBlock controlBlock = null;
        for (UsbDevice device:connCameras.keySet()) {
            if ((device.getVendorId() == usbDevice.getVendorId()) && (device.getProductId() == usbDevice.getProductId())){
                controlBlock = connCameras.get(device);
                break;
            }
        }
        if(controlBlock == null){
            Log.e("UVCCameraPreview","UVCCameraPreview:getUsbControlBlock:find UsbControlBlock failed!");
        }
        return controlBlock;
    }

    public CameraViewInterface getCameraViewInterface(UsbDevice usbDevice){
        CameraViewInterface cameraViewInterface = null;
        USBMonitor.UsbControlBlock controlBlock = getUsbControlBlock(usbDevice);
        for (USBMonitor.UsbControlBlock ctrlBlock:viewCameras.keySet()) {
            if (controlBlock == ctrlBlock){
                cameraViewInterface = viewCameras.get(ctrlBlock);
                break;
            }
        }
        return cameraViewInterface;
    }

    public UVCCameraHandler getUVCCameraHandler(USBMonitor.UsbControlBlock controlBlock){
        UVCCameraHandler handle = null;
        if (handlerCameras.containsKey(controlBlock)){
            handle = handlerCameras.get(controlBlock);
        }
        return handle;
    }

    private UVCCameraHandler getUVCCameraHandler(UsbDevice usbDevice){
        if(usbDevice == null){
            Log.e("UVCCameraPreview","usbDevice is null!");
            return null;
        }
        UVCCameraHandler handle = null;
        USBMonitor.UsbControlBlock controlBlock = getUsbControlBlock(usbDevice);
        if(handlerCameras.containsKey(controlBlock)){
            handle = handlerCameras.get(controlBlock);
        } else {
            Log.e("UVCCameraPreview","camera handler not create!");
        }
        return handle;
    }

    private UVCCameraPreview() {

    }

    public static UVCCameraPreview getInstance() {
        if (mCameraHelper == null) {
            mCameraHelper = new UVCCameraPreview();
        }
        return mCameraHelper;
    }

    public void initUSBMonitor(Activity activity) {
        this.mActivity = activity;

        mUSBMonitor = new USBMonitor(activity.getApplicationContext(), new USBMonitor.OnDeviceConnectListener() {

            // called by checking usb device
            // do request device permission
            @Override
            public void onAttach(UsbDevice device) {
                Log.e("UVCCameraPreview","UVCCameraPreview:onAttach: (vid:" + device.getVendorId() + ", pid:" + device.getProductId() + ")");
                /**
                 * 说明：onAttach以后，如下代码去校验权限，然后发送连接USB设备广播，如果没有如下代码，不会回调onConnect函数
                 */
                if (mUSBMonitor != null) {
                    if(matchUsbDevice(device)){
                        mUSBMonitor.requestPermission(device);
                    }
                }
            }

            // called by taking out usb device
            // do close camera
            @Override
            public void onDettach(UsbDevice device) {
                Log.e("UVCCameraPreview","UVCCameraPreview:onDettach: (vid:" + device.getVendorId() + ", pid:" + device.getProductId() + ")");
            }

            // called by connect to usb camera
            // do open camera,start previewing
            @Override
            public void onConnect(final UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
                Log.e("UVCCameraPreview","UVCCameraPreview:onConnect: (vid:" + device.getVendorId() + ", pid:" + device.getProductId() + ")");
                addConnUsbDevice(device, ctrlBlock);            // 添加连接设备
            }

            // called by disconnect to usb camera
            // do nothing
            @Override
            public void onDisconnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock) {
                Log.e("UVCCameraPreview","UVCCameraPreview:onDisconnect: (vid:" + device.getVendorId() + ", pid:" + device.getProductId() + ")");
                closeUsbDevice(ctrlBlock);              // 关闭工作线程
                removeConnUsbDevice(device);            // 删除连接设备
            }

            @Override
            public void onCancel(UsbDevice device) {
                Log.e("UVCCameraPreview","UVCCameraPreview:onCancel: (vid:" + device.getVendorId() + ", pid:" + device.getProductId() + ")");
            }
        });

    }

    public UVCCameraHandler createUVCCamera(UVCCameraHandler cameraHandler, CameraViewInterface preview) {
        if (preview == null)
            throw new NullPointerException("CameraViewInterface cannot be null!");
        // release resources for initializing camera handler
        if (cameraHandler != null) {
            cameraHandler.release();
            cameraHandler = null;
        }
        // initialize camera handler
        preview.setAspectRatio(previewWidth / (float)previewHeight);
        cameraHandler = UVCCameraHandler.createHandler(mActivity, preview, 1,previewWidth, previewHeight, mFrameFormat);
        return cameraHandler;
    }

    public UVCCameraHandler createUVCCamera(UVCCameraHandler cameraHandler) {

        // release resources for initializing camera handler
        if (cameraHandler != null) {
            cameraHandler.release();
            cameraHandler = null;
        }

        // create preview
        WindowManager windowManager = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
        UVCCameraTextureView preview = new UVCCameraTextureView(mActivity);
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(1, 1,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        lp.gravity = Gravity.LEFT | Gravity.TOP;
        windowManager.addView(preview,lp);

        // initialize camera handler
        cameraHandler = UVCCameraHandler.createHandler(mActivity, preview, 1,previewWidth, previewHeight, mFrameFormat);

        return cameraHandler;
    }

    public void registerUSB() {
        if (mUSBMonitor != null) {
            mUSBMonitor.register();
        }
    }

    public void unregisterUSB() {
        if (mUSBMonitor != null) {
            mUSBMonitor.unregister();
        }
    }

    public boolean checkSupportFlag(UsbDevice usbDevice, final int flag) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        return cameraHandler != null && cameraHandler.checkSupportFlag(flag);
    }

    public int getModelValue(UsbDevice usbDevice, final int flag) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        return cameraHandler != null ? cameraHandler.getValue(flag) : 0;
    }

    public int setModelValue(UsbDevice usbDevice, final int flag, final int value) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        return cameraHandler != null ? cameraHandler.setValue(flag, value) : 0;
    }

    public int resetModelValue(UsbDevice usbDevice, final int flag) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        return cameraHandler != null ? cameraHandler.resetValue(flag) : 0;
    }

    private int getUsbDeviceCount() {
        List<UsbDevice> devList = getUsbDeviceList();
        if (devList == null || devList.size() == 0) {
            return 0;
        }
        return devList.size();
    }

    public List<UsbDevice> getUsbDeviceList() {
        List<DeviceFilter> deviceFilters = DeviceFilter.getDeviceFilters(mActivity.getApplicationContext(), R.xml.device_filter);
        if (mUSBMonitor == null || deviceFilters == null){
            // throw new NullPointerException("mUSBMonitor ="+mUSBMonitor+"deviceFilters=;"+deviceFilters);
            return null;
        }
        // matching all of filter devices
        return mUSBMonitor.getDeviceList(deviceFilters);
    }

    public void captureCut(UsbDevice usbDevice,String savePath) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null && cameraHandler.isOpened()) {
            cameraHandler.captureCut(savePath);
        }
    }

    public void capturePicture(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null && cameraHandler.isOpened()) {
            cameraHandler.captureStill();
        }
    }

    public void capturePicture(UsbDevice usbDevice,String savePath) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null && cameraHandler.isOpened()) {
            cameraHandler.captureStill(savePath);
        }
    }

    public void startPusher(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null && !isPushing(usbDevice)) {
            cameraHandler.startRecording();
        }
    }

    public void stopPusher(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null && isPushing(usbDevice)) {
            cameraHandler.stopRecording();
        }
    }

    public boolean isPushing(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null) {
            return cameraHandler.isRecording();
        }
        return false;
    }

    public boolean isCameraOpened(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null) {
            return cameraHandler.isOpened();
        }
        return false;
    }

    public void release(UsbDevice usbDevice) {
        closeCamera(usbDevice);
        if (mUSBMonitor != null) {
            mUSBMonitor.destroy();
            mUSBMonitor = null;
        }
    }

    public USBMonitor getUSBMonitor() {
        return mUSBMonitor;
    }

    public void setOnUsbMonitoring(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if(cameraHandler != null) {
            cameraHandler.addCallback(new UVCCameraHandler.CameraCallback() {
                @Override
                public void onOpen() {

                }

                @Override
                public void onClose() {

                }

                @Override
                public void onStartPreview() {

                }

                @Override
                public void onStopPreview() {

                }

                @Override
                public void onStartRecording() {

                }

                @Override
                public void onStopRecording() {

                }

                @Override
                public void onError(Exception e) {

                }
            });
        }
    }

    private void openCamera(UVCCameraHandler cameraHandler, USBMonitor.UsbControlBlock ctrlBlock) {
        if (cameraHandler != null && ctrlBlock != null) {
            cameraHandler.open(ctrlBlock);
        }
    }

    public void startPreview(UsbDevice usbDevice, CameraViewInterface cameraView) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        try {
            // wait for camera created
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SurfaceTexture st = cameraView.getSurfaceTexture();
        if (cameraHandler != null) {
            cameraHandler.startPreview(st);
        }
    }

    public void startBackgroundPreview(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        try {
            // wait for camera created
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (cameraHandler != null) {
            cameraHandler.startBackgroundPreview();
        }
    }

    public void stopPreview(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null) {
            cameraHandler.stopPreview();
        }
    }

    public void startCameraFoucs(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null) {
            cameraHandler.startCameraFoucs();
        }
    }

    public void setPreviewSize(UsbDevice usbDevice, int width, int height) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler != null) {
            int[] size = new int[2];
            size[0] = width;
            size[1] = height;
            cameraHandler.startPreviewSize(size);
        }
    }

    public List<Size> getSupportedPreviewSizes(UsbDevice usbDevice) {
        UVCCameraHandler cameraHandler = getUVCCameraHandler(usbDevice);
        if (cameraHandler == null)
            return null;
        return cameraHandler.getSupportedPreviewSizes();
    }

    public void setDefaultPreviewSize(int defaultWidth,int defaultHeight) {
        if(mUSBMonitor != null) {
            throw new IllegalStateException("setDefaultPreviewSize should be call before initMonitor");
        }
        this.previewWidth = defaultWidth;
        this.previewHeight = defaultHeight;
    }

    public void setDefaultFrameFormat(int format) {
        if(mUSBMonitor != null) {
            throw new IllegalStateException("setDefaultFrameFormat should be call before initMonitor");
        }
        this.mFrameFormat = format;
    }

    public int getPreviewWidth() {
        return previewWidth;
    }

    public int getPreviewHeight() {
        return previewHeight;
    }
}
